﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using KorkolaskuriBL;

namespace KorkolaskuriBLTest
{
    delegate bool Test();

    class Program
    {
        static void Main(string[] args)
        {
            RunTest("LoanPayment", TestLoanPayment);
            RunTest("EqualPrincipalPortionStrategy", TestEqualPrincipalPortionStrategy);
            RunTest("EqualTotalPaymentStrategy", TestEqualTotalPaymentStrategy);

            Console.ReadLine();
        }

        static void RunTest(string s, Test test)
        {
            Console.Write("Testing {0}... ", s);
            if (test())
                Console.WriteLine("SUCCEEDED");
            else
                Console.WriteLine("FAILED (debug it you fool)");
        }

        static bool TestLoanPayment()
        {
            var loanPayment = new LoanPayment(1, 1, 1, 1);

            return TestLoanPayment(loanPayment, 1, 1, 1, 1);
        }

        static bool TestLoanPayment(LoanPayment p,
            int period, double principalPortion, double interestPortion, double balance)
        {
            if (p.Period != period)
                return false;

            if (!DoubleEquals(p.PrincipalPortion, principalPortion, 0.005))
                return false;

            if (!DoubleEquals(p.InterestPortion, interestPortion, 0.005))
                return false;

            if (!DoubleEquals(p.TotalPayment, principalPortion + interestPortion, 0.005))
                return false;

            if (!DoubleEquals(p.Balance, balance, 0.005))
                return false;

            return true;
        }

        static bool TestEqualPrincipalPortionStrategy()
        {
            var strategy = new EqualPrincipalPortion();

            // check consistency with itself
            var paymentPeriods = strategy.CalculatePaymentPeriods(10000, 0.12, 12, 1000);
            var periodicPayment =  strategy.CalculatePeriodicPayment(10000, 0.12, 12, paymentPeriods);
            if (strategy.CalculatePaymentPeriods(10000, 0.12, 12, periodicPayment) != paymentPeriods)
                return false;

            // check rightness
            if (paymentPeriods != 10)
                return false;
            if (!DoubleEquals(periodicPayment, 1000, 0.005))
                return false;

            // check payments
            var payments = new List<LoanPayment>(strategy.CalculatePayments(10000, 0.12, 12, 10, 1000));
            if (payments.Count != 10)
                return false;
            if (!TestLoanPayment(payments[0], 1, 1000, 100, 9000))
                return false;
            if (!TestLoanPayment(payments[1], 2, 1000, 90, 8000))
                return false;
            if (!TestLoanPayment(payments[2], 3, 1000, 80, 7000))
                return false;
            if (!TestLoanPayment(payments[3], 4, 1000, 70, 6000))
                return false;
            if (!TestLoanPayment(payments[4], 5, 1000, 60, 5000))
                return false;
            if (!TestLoanPayment(payments[5], 6, 1000, 50, 4000))
                return false;
            if (!TestLoanPayment(payments[6], 7, 1000, 40, 3000))
                return false;
            if (!TestLoanPayment(payments[7], 8, 1000, 30, 2000))
                return false;
            if (!TestLoanPayment(payments[8], 9, 1000, 20, 1000))
                return false;
            if (!TestLoanPayment(payments[9], 10, 1000, 10, 0))
                return false;

            return true;
        }

        static bool TestEqualTotalPaymentStrategy()
        {
            var strategy = new EqualTotalPayment();

            // check consistency with itself
            var paymentPeriods = strategy.CalculatePaymentPeriods(10000, 0.12, 12, 1000);
            var periodicPayment = strategy.CalculatePeriodicPayment(10000, 0.12, 12, paymentPeriods);
            if (strategy.CalculatePaymentPeriods(10000, 0.12, 12, periodicPayment) != paymentPeriods)
                return false;

            // check rightness
            if (paymentPeriods != 11)
                return false;
            if (!DoubleEquals(periodicPayment, 964.54, 0.005))
                return false;

            // check payments
            var payments = new List<LoanPayment>(strategy.CalculatePayments(10000, 0.12, 12, 10, 964.54));
            if (payments.Count != 10)
                return false;
            if (!TestLoanPayment(payments[0], 1, 864.54, 100, 9135.46))
                return false;
            if (!TestLoanPayment(payments[1], 2, 873.19, 91.35, 8262.27))
                return false;
            if (!TestLoanPayment(payments[2], 3, 881.92, 82.62, 7380.36))
                return false;
            if (!TestLoanPayment(payments[3], 4, 890.74, 73.80, 6489.62))
                return false;
            if (!TestLoanPayment(payments[4], 5, 899.64, 64.90, 5589.98))
                return false;
            if (!TestLoanPayment(payments[5], 6, 908.64, 55.90, 4681.34))
                return false;
            if (!TestLoanPayment(payments[6], 7, 917.73, 46.81, 3763.61))
                return false;
            if (!TestLoanPayment(payments[7], 8, 926.90, 37.64, 2836.71))
                return false;
            if (!TestLoanPayment(payments[8], 9, 936.17, 28.37, 1900.53))
                return false;
            if (!TestLoanPayment(payments[9], 10, 945.53, 19.01, 955.00))
                return false;

            return true;
        }

        static bool DoubleEquals(double a, double b, double tolerance)
        {
            return Math.Abs(a - b) <= tolerance;
        }
    }
}
